package com.intretech.tj.gpp.pathfinding

import com.intretech.tj.gpp.Graph
import jakarta.enterprise.context.ApplicationScoped
import jakarta.inject.Inject
import java.util.PriorityQueue

@ApplicationScoped
class Pathfinder @Inject constructor(
    private val graph: Graph,
    private val weightStrategyFactory: WeightStrategyFactory
) {

    data class PathResult(var path: List<Int>, val cost: Long)

    fun findShortestPath(startNodeId: Int, endNodeId: Int): PathResult? {
        val distances = mutableMapOf<Int, Long>().withDefault { Int.MAX_VALUE.toLong() }
        val previousNodes = mutableMapOf<Int, Int>()
        val priorityQueue = PriorityQueue<Pair<Int, Long>>(compareBy { it.second })

        distances[startNodeId] = 0
        priorityQueue.add(startNodeId to 0)

        while (priorityQueue.isNotEmpty()) {
            val (currentNodeId, currentDistance) = priorityQueue.poll()

            if (currentNodeId == endNodeId) {
                return PathResult(reconstructPath(endNodeId, previousNodes), currentDistance)
            }

            if (currentDistance > distances.getValue(currentNodeId)) {
                continue
            }

            graph.getEdgesFrom(currentNodeId).forEach { edge ->

                //使用策略模式实现方法
                val weightStrategy = weightStrategyFactory.get("Normal")
                val dynamicWeight = weightStrategy.calculate(edge)
                val newDistance = currentDistance + dynamicWeight

                if (newDistance < distances.getValue(edge.rfidNodeEnd)) {
                    distances[edge.rfidNodeEnd] = newDistance
                    previousNodes[edge.rfidNodeEnd] = currentNodeId
                    priorityQueue.add(edge.rfidNodeEnd to newDistance)
                }
            }
        }
        return null // No path found
    }


    private fun reconstructPath(endNodeId: Int, previousNodes: Map<Int, Int>): List<Int> {
        val path = mutableListOf<Int>()
        var currentNode: Int? = endNodeId
        while (currentNode != null) {
            path.add(currentNode)
            currentNode = previousNodes[currentNode]
        }
        return path.reversed()
    }
}